Skip to main content

Updating qb-policejob

Because r14-evidence uses many of the event names as qb-policejob to maintain compatability with existing resources, we will need to remove the existing code which provides for evidence generation and handling from the qb-policejob/client/evidence.lua and qb-policejob/server/main.lua files. Additionally, if you have resources which are clones of qb-policejob, such as qb-fib, you will need to repeat this process for those as well! You do not need multiple versions of evidence per job, and can instead include all jobs that need access to evidence under the included Config.AuthorizedJobs.LEO table.

caution

Never make changes to ANY script without making a backup first, you never know what might happen!

Deleting Existing Evidence Files

The first step of our process will be simply deleting the qb-policejob/client/evidence.lua file from our resource, this will prevent the duplication of evidence creation when firing a weapon, and prevent an unecessary hit to performance caused by having an unused evidence system running in qb-policejob.

Deleting evidence.lua from our qb-policejob resource

Removing Existing Server Events

Next, we will need to access our qb-policejob/server/main.lua and remove the following events which are used in the default qb-policejob evidence system. It is important to clear all of these events so that they do not cause errors by accidentally being triggered by events in r14-evidence, and conversley, triggering events in r14-evidence. Please use CTRL-F to search for, and entirely remove, these networked event handlers.

info

If you have resources which are cloned versions of qb-policejob, you may need to search them for events with these names as well!

Server Events to be Removed
'evidence:server:UpdateStatus' 
'evidence:server:CreateBloodDrop'
'evidence:server:CreateFingerDrop'
'evidence:server:ClearBlooddrops'
'evidence:server:AddBlooddropToInventory'
'evidence:server:AddFingerprintToInventory'
'evidence:server:CreateCasing'
'evidence:server:ClearCasings'
'evidence:server:AddCasingToInventory'

Removing Existing Server Callbacks

We will need to remove the police:GetPlayerStatus callback as well from our qb-policejob/server/main.lua file. Because we want this callback available for the Check Status radial menu option, it will need to be moved to r14-evidence so that it can properly supply status effects to the client using it.

qb-policejob Server Callback To Be Removed
QBCore.Functions.CreateCallback('police:GetPlayerStatus', function(_, cb, playerId)
local Player = QBCore.Functions.GetPlayer(playerId)
local statList = {}
if Player then
if PlayerStatus[Player.PlayerData.source] and next(PlayerStatus[Player.PlayerData.source]) then
for k in pairs(PlayerStatus[Player.PlayerData.source]) do
statList[#statList+1] = PlayerStatus[Player.PlayerData.source][k].text
end
end
end
cb(statList)
end)

Removing Existing Server Commands

Lastly we will need to remove the 'clearblood' and 'clearcasings' commands from our qb-policejob server script as these have been replaced by a combined 'clearevidence' command in r14-evidence. Once we have done this, we will finally need to remove the 'takedna' command, as the new resource now allows police to use qb-target in order to collect evidence versus a command.

qb-policejob Server Command To Be Removed
QBCore.Commands.Add("clearblood", Lang:t("commands.clearblood"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("evidence:client:ClearBlooddropsInArea", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)
qb-policejob Server Command To Be Removed
QBCore.Commands.Add("clearcasings", Lang:t("commands.clear_casign"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("evidence:client:ClearCasingsInArea", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)
qb-policejob Server Command To Be Removed
QBCore.Commands.Add("takedna", Lang:t("commands.takedna"), {{name = "id", help = Lang:t('info.player_id')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local OtherPlayer = QBCore.Functions.GetPlayer(tonumber(args[1]))
if not OtherPlayer or Player.PlayerData.job.name ~= "police" or not Player.PlayerData.job.onduty then return end
if Player.Functions.RemoveItem("empty_evidence_bag", 1) then
local info = {
label = Lang:t('info.dna_sample'),
type = "dna",
dnalabel = DnaHash(OtherPlayer.PlayerData.citizenid)
}
if not Player.Functions.AddItem("filled_evidence_bag", 1, false, info) then return end
TriggerClientEvent("inventory:client:ItemBox", src, QBCore.Shared.Items["filled_evidence_bag"], "add")
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.have_evidence_bag"), "error")
end
end)

At this point we have completed the modification of our qb-policejob script and can now continue with our installation process!

Preconfigured Server File

If you are use a recent version of qb-core and a stock qb-policejob, you may be able to use the modified version of qb-policejob/server/main.lua below that comes with all of these these events plus evidence-related functions and variables removed. If you have custom code in your qb-policejob/server/main.lua, please follow the process bove for modifying your sever code!

caution

This code is current as of 4/1/2023, it will not contain updates more recent than this, and may not be compatible with older version of qb-policejob!

Modified qb-policejob/server/main.lua
-- Variables
local Plates = {}
local Objects = {}
local QBCore = exports['qb-core']:GetCoreObject()
local updatingCops = false

-- Functions
local function UpdateBlips()
local dutyPlayers = {}
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and (v.PlayerData.job.name == "police" or v.PlayerData.job.name == "ambulance") and v.PlayerData.job.onduty then
local coords = GetEntityCoords(GetPlayerPed(v.PlayerData.source))
local heading = GetEntityHeading(GetPlayerPed(v.PlayerData.source))
dutyPlayers[#dutyPlayers+1] = {
source = v.PlayerData.source,
label = v.PlayerData.metadata["callsign"],
job = v.PlayerData.job.name,
location = {
x = coords.x,
y = coords.y,
z = coords.z,
w = heading
}
}
end
end
TriggerClientEvent("police:client:UpdateBlips", -1, dutyPlayers)
end

local function CreateObjectId()
if Objects then
local objectId = math.random(10000, 99999)
while Objects[objectId] do
objectId = math.random(10000, 99999)
end
return objectId
else
local objectId = math.random(10000, 99999)
return objectId
end
end

local function IsVehicleOwned(plate)
local result = MySQL.scalar.await('SELECT plate FROM player_vehicles WHERE plate = ?', {plate})
return result
end

local function GetCurrentCops()
local amount = 0
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == "police" and v.PlayerData.job.onduty then
amount += 1
end
end
return amount
end

local function DnaHash(s)
local h = string.gsub(s, ".", function(c)
return string.format("%02x", string.byte(c))
end)
return h
end

-- Commands
QBCore.Commands.Add("spikestrip", Lang:t("commands.place_spike"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent('police:client:SpawnSpikeStrip', src)
end
end)

QBCore.Commands.Add("grantlicense", Lang:t("commands.license_grant"), {{name = "id", help = Lang:t('info.player_id')}, {name = "license", help = Lang:t('info.license_type')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.grade.level >= Config.LicenseRank then
if args[2] == "driver" or args[2] == "weapon" then
local SearchedPlayer = QBCore.Functions.GetPlayer(tonumber(args[1]))
if not SearchedPlayer then return end
local licenseTable = SearchedPlayer.PlayerData.metadata["licences"]
if licenseTable[args[2]] then
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.license_already"), "error")
return
end
licenseTable[args[2]] = true
SearchedPlayer.Functions.SetMetaData("licences", licenseTable)
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("success.granted_license"), "success")
TriggerClientEvent('QBCore:Notify', src, Lang:t("success.grant_license"), "success")
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.error_license_type"), "error")
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.rank_license"), "error")
end
end)

QBCore.Commands.Add("revokelicense", Lang:t("commands.license_revoke"), {{name = "id", help = Lang:t('info.player_id')}, {name = "license", help = Lang:t('info.license_type')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.grade.level >= Config.LicenseRank then
if args[2] == "driver" or args[2] == "weapon" then
local SearchedPlayer = QBCore.Functions.GetPlayer(tonumber(args[1]))
if not SearchedPlayer then return end
local licenseTable = SearchedPlayer.PlayerData.metadata["licences"]
if not licenseTable[args[2]] then
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.error_license"), "error")
return
end
licenseTable[args[2]] = false
SearchedPlayer.Functions.SetMetaData("licences", licenseTable)
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("error.revoked_license"), "error")
TriggerClientEvent('QBCore:Notify', src, Lang:t("success.revoke_license"), "success")
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.error_license"), "error")
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.rank_revoke"), "error")
end
end)

QBCore.Commands.Add("pobject", Lang:t("commands.place_object"), {{name = "type",help = Lang:t("info.poobject_object")}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local type = args[1]:lower()
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
if type == "cone" then
TriggerClientEvent("police:client:spawnCone", src)
elseif type == "barrier" then
TriggerClientEvent("police:client:spawnBarrier", src)
elseif type == "roadsign" then
TriggerClientEvent("police:client:spawnRoadSign", src)
elseif type == "tent" then
TriggerClientEvent("police:client:spawnTent", src)
elseif type == "light" then
TriggerClientEvent("police:client:spawnLight", src)
elseif type == "delete" then
TriggerClientEvent("police:client:deleteObject", src)
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("cuff", Lang:t("commands.cuff_player"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:CuffPlayer", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("escort", Lang:t("commands.escort"), {}, false, function(source)
local src = source
TriggerClientEvent("police:client:EscortPlayer", src)
end)

QBCore.Commands.Add("callsign", Lang:t("commands.callsign"), {{name = "name", help = Lang:t('info.callsign_name')}}, false, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.SetMetaData("callsign", table.concat(args, " "))
end)

QBCore.Commands.Add("jail", Lang:t("commands.jail_player"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:JailPlayer", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("unjail", Lang:t("commands.unjail_player"), {{name = "id", help = Lang:t('info.player_id')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
local playerId = tonumber(args[1])
TriggerClientEvent("prison:client:UnjailPerson", playerId)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("seizecash", Lang:t("commands.seizecash"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:SeizeCash", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("sc", Lang:t("commands.softcuff"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:CuffPlayerSoft", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("cam", Lang:t("commands.camera"), {{name = "camid", help = Lang:t('info.camera_id')}}, false, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:ActiveCamera", src, tonumber(args[1]))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("flagplate", Lang:t("commands.flagplate"), {{name = "plate", help = Lang:t('info.plate_number')}, {name = "reason", help = Lang:t('info.flag_reason')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
local reason = {}
for i = 2, #args, 1 do
reason[#reason+1] = args[i]
end
Plates[args[1]:upper()] = {
isflagged = true,
reason = table.concat(reason, " ")
}
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.vehicle_flagged", {vehicle = args[1]:upper(), reason = table.concat(reason, " ")}))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("unflagplate", Lang:t("commands.unflagplate"), {{name = "plate", help = Lang:t('info.plate_number')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
if Plates and Plates[args[1]:upper()] then
if Plates[args[1]:upper()].isflagged then
Plates[args[1]:upper()].isflagged = false
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.unflag_vehicle", {vehicle = args[1]:upper()}))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.vehicle_not_flag"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.vehicle_not_flag"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("plateinfo", Lang:t("commands.plateinfo"), {{name = "plate", help = Lang:t('info.plate_number')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
if Plates and Plates[args[1]:upper()] then
if Plates[args[1]:upper()].isflagged then
TriggerClientEvent('QBCore:Notify', src, Lang:t('success.vehicle_flagged', {plate = args[1]:upper(), reason = Plates[args[1]:upper()].reason}), 'success')
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.vehicle_not_flag"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.vehicle_not_flag"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("depot", Lang:t("commands.depot"), {{name = "price", help = Lang:t('info.impound_price')}}, false, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:ImpoundVehicle", src, false, tonumber(args[1]))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("impound", Lang:t("commands.impound"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:ImpoundVehicle", src, true)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("paytow", Lang:t("commands.paytow"), {{name = "id", help = Lang:t('info.player_id')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
local playerId = tonumber(args[1])
local OtherPlayer = QBCore.Functions.GetPlayer(playerId)
if OtherPlayer then
if OtherPlayer.PlayerData.job.name == "tow" then
OtherPlayer.Functions.AddMoney("bank", 500, "police-tow-paid")
TriggerClientEvent('QBCore:Notify', OtherPlayer.PlayerData.source, Lang:t("success.tow_paid"), 'success')
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.tow_driver_paid"))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_towdriver"), 'error')
end
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("paylawyer", Lang:t("commands.paylawyer"), {{name = "id",help = Lang:t('info.player_id')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" or Player.PlayerData.job.name == "judge" then
local playerId = tonumber(args[1])
local OtherPlayer = QBCore.Functions.GetPlayer(playerId)
if not OtherPlayer then return end
if OtherPlayer.PlayerData.job.name == "lawyer" then
OtherPlayer.Functions.AddMoney("bank", 500, "police-lawyer-paid")
TriggerClientEvent('QBCore:Notify', OtherPlayer.PlayerData.source, Lang:t("success.tow_paid"), 'success')
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.paid_lawyer"))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_lawyer"), "error")
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add('fine', Lang:t("commands.fine"), {{name = 'id', help = Lang:t('info.player_id')}, {name = 'amount', help = Lang:t('info.amount')}}, false, function(source, args)
local biller = QBCore.Functions.GetPlayer(source)
local billed = QBCore.Functions.GetPlayer(tonumber(args[1]))
local amount = tonumber(args[2])
if biller.PlayerData.job.name == "police" then
if billed ~= nil then
if biller.PlayerData.citizenid ~= billed.PlayerData.citizenid then
if amount and amount > 0 then
if billed.Functions.RemoveMoney('bank', amount, "paid-fine") then
TriggerClientEvent('QBCore:Notify', source, Lang:t("info.fine_issued"), 'success')
TriggerClientEvent('QBCore:Notify', billed.PlayerData.source, Lang:t("info.received_fine"))
exports['qb-management']:AddMoney(biller.PlayerData.job.name, amount)
elseif billed.Functions.RemoveMoney('cash', amount, "paid-fine") then
TriggerClientEvent('QBCore:Notify', source, Lang:t("info.fine_issued"), 'success')
TriggerClientEvent('QBCore:Notify', billed.PlayerData.source, Lang:t("info.received_fine"))
exports['qb-management']:AddMoney(biller.PlayerData.job.name, amount)
else
MySQL.Async.insert('INSERT INTO phone_invoices (citizenid, amount, society, sender, sendercitizenid) VALUES (?, ?, ?, ?, ?)',{billed.PlayerData.citizenid, amount, biller.PlayerData.job.name, biller.PlayerData.charinfo.firstname, biller.PlayerData.citizenid}, function(id)
if id then
TriggerClientEvent('qb-phone:client:AcceptorDenyInvoice', billed.PlayerData.source, id, biller.PlayerData.charinfo.firstname, biller.PlayerData.job.name, biller.PlayerData.citizenid, amount, GetInvokingResource())
end
end)
TriggerClientEvent('qb-phone:RefreshPhone', billed.PlayerData.source)
end
else
TriggerClientEvent('QBCore:Notify', source, Lang:t("error.amount_higher"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', source, Lang:t("error.fine_yourself"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', source, Lang:t("error.not_online"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', source, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("anklet", Lang:t("commands.anklet"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:CheckDistance", src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("ankletlocation", Lang:t("commands.ankletlocation"), {{name = "cid", help = Lang:t('info.citizen_id')}}, true, function(source, args)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
local citizenid = args[1]
local Target = QBCore.Functions.GetPlayerByCitizenId(citizenid)
if not Target then return end
if Target.PlayerData.metadata["tracker"] then
TriggerClientEvent("police:client:SendTrackerLocation", Target.PlayerData.source, src)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.no_anklet"), 'error')
end
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

QBCore.Commands.Add("takedrivinglicense", Lang:t("commands.drivinglicense"), {}, false, function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty then
TriggerClientEvent("police:client:SeizeDriverLicense", source)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.on_duty_police_only"), 'error')
end
end)

RegisterNetEvent('police:server:SendTrackerLocation', function(coords, requestId)
local Target = QBCore.Functions.GetPlayer(source)
local msg = Lang:t('info.target_location', {firstname = Target.PlayerData.charinfo.firstname, lastname = Target.PlayerData.charinfo.lastname})
local alertData = {
title = Lang:t('info.anklet_location'),
coords = {
x = coords.x,
y = coords.y,
z = coords.z
},
description = msg
}
TriggerClientEvent("police:client:TrackerMessage", requestId, msg, coords)
TriggerClientEvent("qb-phone:client:addPoliceAlert", requestId, alertData)
end)

QBCore.Commands.Add('911p', Lang:t("commands.police_report"), {{name='message', help= Lang:t("commands.message_sent")}}, false, function(source, args)
local src = source
local message
if args[1] then message = table.concat(args, " ") else message = Lang:t("commands.civilian_call") end
local ped = GetPlayerPed(src)
local coords = GetEntityCoords(ped)
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == 'police' and v.PlayerData.job.onduty then
local alertData = {title = Lang:t("commands.emergency_call"), coords = {x = coords.x, y = coords.y, z = coords.z}, description = message}
TriggerClientEvent("qb-phone:client:addPoliceAlert", v.PlayerData.source, alertData)
TriggerClientEvent('police:client:policeAlert', v.PlayerData.source, coords, message)
end
end
end)

-- Items
QBCore.Functions.CreateUseableItem("handcuffs", function(source)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player.Functions.GetItemByName("handcuffs") then return end
TriggerClientEvent("police:client:CuffPlayerSoft", src)
end)

QBCore.Functions.CreateUseableItem("moneybag", function(source, item)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player or not Player.Functions.GetItemByName("moneybag") or not item.info or item.info == "" or Player.PlayerData.job.name == "police" or not Player.Functions.RemoveItem("moneybag", 1, item.slot) then return end
Player.Functions.AddMoney("cash", tonumber(item.info.cash), "used-moneybag")
end)

-- Callbacks
QBCore.Functions.CreateCallback('police:server:isPlayerDead', function(_, cb, playerId)
local Player = QBCore.Functions.GetPlayer(playerId)
cb(Player.PlayerData.metadata["isdead"])
end)

QBCore.Functions.CreateCallback('police:IsSilencedWeapon', function(source, cb, weapon)
local Player = QBCore.Functions.GetPlayer(source)
local itemInfo = Player.Functions.GetItemByName(QBCore.Shared.Weapons[weapon]["name"])
local retval = false
if itemInfo then
if itemInfo.info and itemInfo.info.attachments then
for k in pairs(itemInfo.info.attachments) do
if itemInfo.info.attachments[k].component == "COMPONENT_AT_AR_SUPP_02" or
itemInfo.info.attachments[k].component == "COMPONENT_AT_AR_SUPP" or
itemInfo.info.attachments[k].component == "COMPONENT_AT_PI_SUPP_02" or
itemInfo.info.attachments[k].component == "COMPONENT_AT_PI_SUPP" then
retval = true
end
end
end
end
cb(retval)
end)

QBCore.Functions.CreateCallback('police:GetDutyPlayers', function(_, cb)
local dutyPlayers = {}
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == "police" and v.PlayerData.job.onduty then
dutyPlayers[#dutyPlayers+1] = {
source = v.PlayerData.source,
label = v.PlayerData.metadata["callsign"],
job = v.PlayerData.job.name
}
end
end
cb(dutyPlayers)
end)

QBCore.Functions.CreateCallback('police:GetImpoundedVehicles', function(_, cb)
local vehicles = {}
MySQL.query('SELECT * FROM player_vehicles WHERE state = ?', {2}, function(result)
if result[1] then
vehicles = result
end
cb(vehicles)
end)
end)

QBCore.Functions.CreateCallback('police:IsPlateFlagged', function(_, cb, plate)
local retval = false
if Plates and Plates[plate] then
if Plates[plate].isflagged then
retval = true
end
end
cb(retval)
end)

QBCore.Functions.CreateCallback('police:GetCops', function(_, cb)
local amount = 0
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == "police" and v.PlayerData.job.onduty then
amount = amount + 1
end
end
cb(amount)
end)

QBCore.Functions.CreateCallback('police:server:IsPoliceForcePresent', function(_, cb)
local retval = false
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == "police" and v.PlayerData.job.grade.level >= 2 then
retval = true
break
end
end
cb(retval)
end)

-- Events
AddEventHandler('onResourceStart', function(resourceName)
if resourceName == GetCurrentResourceName() then
CreateThread(function()
MySQL.query("DELETE FROM stashitems WHERE stash = 'policetrash'")
end)
end
end)

RegisterNetEvent('police:server:policeAlert', function(text)
local src = source
local ped = GetPlayerPed(src)
local coords = GetEntityCoords(ped)
local players = QBCore.Functions.GetQBPlayers()
for _, v in pairs(players) do
if v and v.PlayerData.job.name == 'police' and v.PlayerData.job.onduty then
local alertData = {title = Lang:t('info.new_call'), coords = {x = coords.x, y = coords.y, z = coords.z}, description = text}
TriggerClientEvent("qb-phone:client:addPoliceAlert", v.PlayerData.source, alertData)
TriggerClientEvent('police:client:policeAlert', v.PlayerData.source, coords, text)
end
end
end)

RegisterNetEvent('police:server:TakeOutImpound', function(plate, garage)
local src = source
local playerPed = GetPlayerPed(src)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = Config.Locations["impound"][garage]
if #(playerCoords - targetCoords) > 10.0 then return DropPlayer(src, "Attempted exploit abuse") end

MySQL.update('UPDATE player_vehicles SET state = ? WHERE plate = ?', {0, plate})
TriggerClientEvent('QBCore:Notify', src, Lang:t("success.impound_vehicle_removed"), 'success')
end)

RegisterNetEvent('police:server:CuffPlayer', function(playerId, isSoftcuff)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(src)
local CuffedPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not CuffedPlayer or (not Player.Functions.GetItemByName("handcuffs") and Player.PlayerData.job.name ~= "police") then return end

TriggerClientEvent("police:client:GetCuffed", CuffedPlayer.PlayerData.source, Player.PlayerData.source, isSoftcuff)
end)

RegisterNetEvent('police:server:EscortPlayer', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(source)
local EscortPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not EscortPlayer then return end

if (Player.PlayerData.job.name == "police" or Player.PlayerData.job.name == "ambulance") or (EscortPlayer.PlayerData.metadata["ishandcuffed"] or EscortPlayer.PlayerData.metadata["isdead"] or EscortPlayer.PlayerData.metadata["inlaststand"]) then
TriggerClientEvent("police:client:GetEscorted", EscortPlayer.PlayerData.source, Player.PlayerData.source)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_cuffed_dead"), 'error')
end
end)

RegisterNetEvent('police:server:KidnapPlayer', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(source)
local EscortPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not EscortPlayer then return end

if EscortPlayer.PlayerData.metadata["ishandcuffed"] or EscortPlayer.PlayerData.metadata["isdead"] or EscortPlayer.PlayerData.metadata["inlaststand"] then
TriggerClientEvent("police:client:GetKidnappedTarget", EscortPlayer.PlayerData.source, Player.PlayerData.source)
TriggerClientEvent("police:client:GetKidnappedDragger", Player.PlayerData.source, EscortPlayer.PlayerData.source)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_cuffed_dead"), 'error')
end
end)

RegisterNetEvent('police:server:SetPlayerOutVehicle', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local EscortPlayer = QBCore.Functions.GetPlayer(playerId)
if not QBCore.Functions.GetPlayer(src) or not EscortPlayer then return end

if EscortPlayer.PlayerData.metadata["ishandcuffed"] or EscortPlayer.PlayerData.metadata["isdead"] then
TriggerClientEvent("police:client:SetOutVehicle", EscortPlayer.PlayerData.source)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_cuffed_dead"), 'error')
end
end)

RegisterNetEvent('police:server:PutPlayerInVehicle', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local EscortPlayer = QBCore.Functions.GetPlayer(playerId)
if not QBCore.Functions.GetPlayer(src) or not EscortPlayer then return end

if EscortPlayer.PlayerData.metadata["ishandcuffed"] or EscortPlayer.PlayerData.metadata["isdead"] then
TriggerClientEvent("police:client:PutInVehicle", EscortPlayer.PlayerData.source)
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_cuffed_dead"), 'error')
end
end)

RegisterNetEvent('police:server:BillPlayer', function(playerId, price)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(src)
local OtherPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not OtherPlayer or Player.PlayerData.job.name ~= "police" then return end

OtherPlayer.Functions.RemoveMoney("bank", price, "paid-bills")
exports['qb-management']:AddMoney("police", price)
TriggerClientEvent('QBCore:Notify', OtherPlayer.PlayerData.source, Lang:t("info.fine_received", {fine = price}))
end)

RegisterNetEvent('police:server:JailPlayer', function(playerId, time)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(src)
local OtherPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not OtherPlayer or Player.PlayerData.job.name ~= "police" then return end

local currentDate = os.date("*t")
if currentDate.day == 31 then
currentDate.day = 30
end

OtherPlayer.Functions.SetMetaData("injail", time)
OtherPlayer.Functions.SetMetaData("criminalrecord", {
["hasRecord"] = true,
["date"] = currentDate
})
TriggerClientEvent("police:client:SendToJail", OtherPlayer.PlayerData.source, time)
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.sent_jail_for", {time = time}))
end)

RegisterNetEvent('police:server:SetHandcuffStatus', function(isHandcuffed)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if Player then
Player.Functions.SetMetaData("ishandcuffed", isHandcuffed)
end
end)

RegisterNetEvent('heli:spotlight', function(state)
local serverID = source
TriggerClientEvent('heli:spotlight', -1, serverID, state)
end)

-- RegisterNetEvent('police:server:FlaggedPlateTriggered', function(camId, plate, street1, street2, blipSettings)
-- local src = source
-- for k, v in pairs(QBCore.Functions.GetPlayers()) do
-- local Player = QBCore.Functions.GetPlayer(v)
-- if Player then
-- if (Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty) then
-- if street2 then
-- TriggerClientEvent("112:client:SendPoliceAlert", v, "flagged", {
-- camId = camId,
-- plate = plate,
-- streetLabel = street1 .. " " .. street2
-- }, blipSettings)
-- else
-- TriggerClientEvent("112:client:SendPoliceAlert", v, "flagged", {
-- camId = camId,
-- plate = plate,
-- streetLabel = street1
-- }, blipSettings)
-- end
-- end
-- end
-- end
-- end)

RegisterNetEvent('police:server:SearchPlayer', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local SearchedPlayer = QBCore.Functions.GetPlayer(playerId)
if not QBCore.Functions.GetPlayer(src) or not SearchedPlayer then return end

TriggerClientEvent('QBCore:Notify', src, Lang:t("info.cash_found", {cash = SearchedPlayer.PlayerData.money["cash"]}))
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("info.being_searched"))
end)

RegisterNetEvent('police:server:SeizeCash', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(src)
local SearchedPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not SearchedPlayer then return end

local moneyAmount = SearchedPlayer.PlayerData.money["cash"]
local info = { cash = moneyAmount }
SearchedPlayer.Functions.RemoveMoney("cash", moneyAmount, "police-cash-seized")
Player.Functions.AddItem("moneybag", 1, false, info)
TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items["moneybag"], "add")
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("info.cash_confiscated"))
end)

RegisterNetEvent('police:server:SeizeDriverLicense', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local SearchedPlayer = QBCore.Functions.GetPlayer(playerId)
if not QBCore.Functions.GetPlayer(src) or not SearchedPlayer then return end

local driverLicense = SearchedPlayer.PlayerData.metadata["licences"]["driver"]
if driverLicense then
local licenses = {["driver"] = false, ["business"] = SearchedPlayer.PlayerData.metadata["licences"]["business"]}
SearchedPlayer.Functions.SetMetaData("licences", licenses)
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("info.driving_license_confiscated"))
else
TriggerClientEvent('QBCore:Notify', src, Lang:t("error.no_driver_license"), 'error')
end
end)

RegisterNetEvent('police:server:RobPlayer', function(playerId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(playerId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Player = QBCore.Functions.GetPlayer(src)
local SearchedPlayer = QBCore.Functions.GetPlayer(playerId)
if not Player or not SearchedPlayer then return end

local money = SearchedPlayer.PlayerData.money["cash"]
Player.Functions.AddMoney("cash", money, "police-player-robbed")
SearchedPlayer.Functions.RemoveMoney("cash", money, "police-player-robbed")
TriggerClientEvent('QBCore:Notify', SearchedPlayer.PlayerData.source, Lang:t("info.cash_robbed", {money = money}))
TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, Lang:t("info.stolen_money", {stolen = money}))
end)

RegisterNetEvent('police:server:UpdateBlips', function()
-- KEEP FOR REF BUT NOT NEEDED ANYMORE.
end)

RegisterNetEvent('police:server:spawnObject', function(type)
local src = source
local objectId = CreateObjectId()
Objects[objectId] = type
TriggerClientEvent("police:client:spawnObject", src, objectId, type, src)
end)

RegisterNetEvent('police:server:deleteObject', function(objectId)
TriggerClientEvent('police:client:removeObject', -1, objectId)
end)

RegisterNetEvent('police:server:Impound', function(plate, fullImpound, price, body, engine, fuel)
local src = source
price = price and price or 0
if IsVehicleOwned(plate) then
if not fullImpound then
MySQL.query(
'UPDATE player_vehicles SET state = ?, depotprice = ?, body = ?, engine = ?, fuel = ? WHERE plate = ?',
{0, price, body, engine, fuel, plate})
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.vehicle_taken_depot", {price = price}))
else
MySQL.query(
'UPDATE player_vehicles SET state = ?, body = ?, engine = ?, fuel = ? WHERE plate = ?',
{2, body, engine, fuel, plate})
TriggerClientEvent('QBCore:Notify', src, Lang:t("info.vehicle_seized"))
end
end
end)

RegisterNetEvent('police:server:UpdateCurrentCops', function()
local amount = 0
local players = QBCore.Functions.GetQBPlayers()
if updatingCops then return end
updatingCops = true
for _, v in pairs(players) do
if v and v.PlayerData.job.name == "police" and v.PlayerData.job.onduty then
amount += 1
end
end
TriggerClientEvent("police:SetCopCount", -1, amount)
updatingCops = false
end)

RegisterNetEvent('police:server:showFingerprint', function(playerId)
local src = source
TriggerClientEvent('police:client:showFingerprint', playerId, src)
TriggerClientEvent('police:client:showFingerprint', src, playerId)
end)

RegisterNetEvent('police:server:showFingerprintId', function(sessionId)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local fid = Player.PlayerData.metadata["fingerprint"]
TriggerClientEvent('police:client:showFingerprintId', sessionId, fid)
TriggerClientEvent('police:client:showFingerprintId', src, fid)
end)

RegisterNetEvent('police:server:SetTracker', function(targetId)
local src = source
local playerPed = GetPlayerPed(src)
local targetPed = GetPlayerPed(targetId)
local playerCoords = GetEntityCoords(playerPed)
local targetCoords = GetEntityCoords(targetPed)
if #(playerCoords - targetCoords) > 2.5 then return DropPlayer(src, "Attempted exploit abuse") end

local Target = QBCore.Functions.GetPlayer(targetId)
if not QBCore.Functions.GetPlayer(src) or not Target then return end

local TrackerMeta = Target.PlayerData.metadata["tracker"]
if TrackerMeta then
Target.Functions.SetMetaData("tracker", false)
TriggerClientEvent('QBCore:Notify', targetId, Lang:t("success.anklet_taken_off"), 'success')
TriggerClientEvent('QBCore:Notify', src, Lang:t("success.took_anklet_from", {firstname = Target.PlayerData.charinfo.firstname, lastname = Target.PlayerData.charinfo.lastname}), 'success')
TriggerClientEvent('police:client:SetTracker', targetId, false)
else
Target.Functions.SetMetaData("tracker", true)
TriggerClientEvent('QBCore:Notify', targetId, Lang:t("success.put_anklet"), 'success')
TriggerClientEvent('QBCore:Notify', src, Lang:t("success.put_anklet_on", {firstname = Target.PlayerData.charinfo.firstname, lastname = Target.PlayerData.charinfo.lastname}), 'success')
TriggerClientEvent('police:client:SetTracker', targetId, true)
end
end)

RegisterNetEvent('police:server:SyncSpikes', function(table)
TriggerClientEvent('police:client:SyncSpikes', -1, table)
end)

-- Threads
CreateThread(function()
while true do
Wait(1000 * 60 * 10)
local curCops = GetCurrentCops()
TriggerClientEvent("police:SetCopCount", -1, curCops)
end
end)

CreateThread(function()
while true do
Wait(5000)
UpdateBlips()
end
end)